﻿using System;
using System.Collections.Generic;
using System.Linq;

using System.Diagnostics;
using System.Runtime.InteropServices;

namespace Graphene
{
    public delegate IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam);

    public delegate void RotateDXCameraInModelDelegate(float byY, float byX);   

    public class MouseInterceptor
    {
        private static float MOUSE_SENSITIVITY = 0.003f;                   

        public static void SetRotateDelegate(RotateDXCameraInModelDelegate d)
        {
            rotateDelegate = d;
        }

        public static IntPtr SetHook(LowLevelMouseProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_MOUSE_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }
        
        public static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));   
            
            if (trackMouseMovement && nCode >= 0 && MouseMessages.WM_MOUSEMOVE == (MouseMessages)wParam)                
            {   
                float byY = (hookStruct.pt.x - mousePositionX) * MOUSE_SENSITIVITY;
                float byX = (hookStruct.pt.y - mousePositionY) * MOUSE_SENSITIVITY;

                //hookStruct.pt.x = 640;
                //hookStruct.pt.y = 480;

                //SetCursorPos(640, 480);
                //System.Windows.Forms.Cursor.Position = new System.Drawing.Point(640, 480);
                    
                mousePositionX = hookStruct.pt.x;
                mousePositionY = hookStruct.pt.y;
                    
                //mousePositionX = 640;
                //mousePositionY = 480;

                rotateDelegate(byY, byX);
            }

            mousePositionX = hookStruct.pt.x;
            mousePositionY = hookStruct.pt.y;
                        
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }

        public static readonly LowLevelMouseProc llmp = new LowLevelMouseProc(MouseInterceptor.HookCallback);
        public static IntPtr _hookID = IntPtr.Zero;
        private const int WH_MOUSE_LL = 14;
        private static RotateDXCameraInModelDelegate rotateDelegate;

        private static int moved = 0;

        private static int mousePositionX = 640;
        private static int mousePositionY = 480;                

        private enum MouseMessages
        {
            WM_LBUTTONDOWN = 0x0201,
            WM_LBUTTONUP = 0x0202,
            WM_MOUSEMOVE = 0x0200,
            WM_MOUSEWHEEL = 0x020A,
            WM_RBUTTONDOWN = 0x0204,
            WM_RBUTTONUP = 0x0205
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct POINT
        {
            public int x;
            public int y;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MSLLHOOKSTRUCT
        {
            public POINT pt;
            public uint mouseData;
            public uint flags;
            public uint time;
            public IntPtr dwExtraInfo;
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool UnhookWindowsHookEx(IntPtr hhk);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);



        [DllImport("user32.dll")]
        private static extern bool SetCursorPos(int X, int Y);

        private static bool trackMouseMovement = false;

        public static bool TrackMouseMovement
        {
            set { MouseInterceptor.trackMouseMovement = value; }
        }
    }
}
